package com.boyadegroot.divelog.ui.diver.tabs;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;

import com.boyadegroot.divelog.R;
import com.boyadegroot.divelog.data.DiveLogData;
import com.boyadegroot.divelog.data.DiveLogDatabaseContract;
import com.boyadegroot.divelog.data.DiveLogProvider;
import com.boyadegroot.divelog.models.DiveLocation;
import com.boyadegroot.divelog.models.Diver;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class DiverLocations extends Fragment implements OnMapReadyCallback, LoaderManager.LoaderCallbacks<Cursor> {

    private static final String TAG = DiverLocations.class.getName();

    private static final String MAPS_IMAGE_FOLDER = "diver_maps";
    private static final String MAPS_PORTRAIT_SUBFOLDER = "portrait";
    private static final String MAPS_STATIC_URL = "http://maps.google.com/maps/api/staticmap";
    private static final String GOOGLE_API_KEY = "AIzaSyCPAvhSH5ATXkv7-59KSVMrAE5LVFUi91o";

    private Diver mDiver;
    private DiverLocationsAdapter mAdapter;

    public static DiverLocations newInstance(Diver diver) {
        DiverLocations instance = new DiverLocations();
        Bundle args = new Bundle();
        args.putParcelable("diver", diver);
        instance.setArguments(args);
        return instance;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle args = getArguments();
        if (args != null) {
            mDiver = getArguments().getParcelable("diver");
        }
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Activity activity = getActivity();
        ListView locationsList = (ListView) activity.findViewById(R.id.locationslist);
        if (locationsList != null) {
            mAdapter = new DiverLocationsAdapter(activity, R.layout.listitem_location);
            locationsList.setAdapter(mAdapter);
            getLoaderManager().initLoader(0, null, this);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View fragmentView = inflater.inflate(R.layout.pager_diver_tab_locations, container, false);
        createView(fragmentView);
        return fragmentView;
    }

    private File getMapDirectory() {
        Resources resources = getResources();
        Configuration configuration = resources.getConfiguration();
        int orientation = configuration.orientation;
        Activity activity = getActivity();
        File mapDirectory;
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            mapDirectory = new File(activity.getDir(MAPS_IMAGE_FOLDER, Context.MODE_PRIVATE), MAPS_PORTRAIT_SUBFOLDER);
            mapDirectory.mkdir();
        } else {
            mapDirectory = activity.getDir(MAPS_IMAGE_FOLDER, Context.MODE_PRIVATE);
        }
        return mapDirectory;
    }

    private File getMapImageFile() {
        long diverId = mDiver.getId();
        String mapName = Long.toString(diverId);
        File mapDirectory = getMapDirectory();
        File mapFile = new File(mapDirectory, mapName);
        return mapFile;
    }

    private boolean setMapImage() {
        Activity activity = getActivity();
        ImageView staticMap = (ImageView) activity.findViewById(R.id.staticmap);
        File imageFile = getMapImageFile();
        if (imageFile.exists()) {
            String imagePath = imageFile.getAbsolutePath();
            Bitmap image = BitmapFactory.decodeFile(imagePath);
            if (image == null) {
                Log.e(TAG, "Could not read image: " + imagePath);
                return false;
            }
            staticMap.setImageBitmap(image);
            return true;
        } else {
            staticMap.setImageBitmap(null);
            return false;
        }
    }

    private List<String> getMapCoordinates() {
        Activity activity = getActivity();
        DiveLogData data = DiveLogData.getInstance(activity);
        List<String> coordinatesList = data.getDiverLocationCoordinates(mDiver.getId(), ",");
        return coordinatesList;
    }

    private void onImageDownloaded(String mapUrl) {
        Activity activity = getActivity();
        Toast.makeText(activity, getString(R.string.toast_mapdownloaded), Toast.LENGTH_SHORT).show();
        saveMapUrl(mapUrl);
        setMapImage();
    }

    private String getSavedMapUrl() {
        Resources resources = getResources();
        Configuration configuration = resources.getConfiguration();
        Activity activity = getActivity();
        SharedPreferences preferences = activity.getPreferences(Context.MODE_PRIVATE);
        int orientation = configuration.orientation;
        String savedMapUrlKey;
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            savedMapUrlKey = getString(R.string.saved_mapurl_portrait);
        } else {
            savedMapUrlKey = getString(R.string.saved_mapurl_landscape);
        }
        String savedMapUrl = preferences.getString(savedMapUrlKey, null);
        return savedMapUrl;
    }

    private void deleteSavedMapUrl() {
        saveMapUrl(null);
    }

    private void saveMapUrl(String url) {
        Resources resources = getResources();
        Configuration configuration = resources.getConfiguration();
        Activity activity = getActivity();
        SharedPreferences preferences = activity.getPreferences(Context.MODE_PRIVATE);
        SharedPreferences.Editor preferencesEditor = preferences.edit();
        int orientation = configuration.orientation;
        String mapUrlKey;
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            mapUrlKey = getString(R.string.saved_mapurl_portrait);
        } else {
            mapUrlKey = getString(R.string.saved_mapurl_landscape);
        }
        if (url == null) {
            preferencesEditor.remove(mapUrlKey);
        } else {
            preferencesEditor.putString(mapUrlKey, url);
        }
        preferencesEditor.apply();
    }

    private String getMapUrl(int width, int height, List<String> coordinatesList) {
        StringBuilder url = new StringBuilder(MAPS_STATIC_URL);
        url.append("?center=25,15");
        url.append("&zoom=1");
        url.append("&scale=2");
        url.append("&size=" + (width / 2) + "x" + (height / 2)); // half the width/height because the scale is 2
        url.append("&sensor=false");
        url.append("&key=" + GOOGLE_API_KEY);
        url.append("&markers=");
        String coordinates = TextUtils.join("|", coordinatesList);
        url.append(coordinates);
        String mapUrl = url.toString();
        return mapUrl;
    }

    private void createView(View fragmentView) {
        Resources resources = getResources();
        Configuration configuration = resources.getConfiguration();
        int orientation = configuration.orientation;
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            loadStaticMap(fragmentView);
        } else {
            loadDynamicMap();
        }
    }

    private void loadStaticMap(View fragmentView) {
        final ImageView staticMap = (ImageView) fragmentView.findViewById(R.id.staticmap);
        staticMap.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                if (left == 0 && top == 0 && right == 0 && bottom == 0) {
                    return;
                }
                int mapWidth = staticMap.getMeasuredWidth();
                int mapHeight = staticMap.getMeasuredHeight();
                List<String> mapCoordinates = getMapCoordinates();
                String mapUrl = getMapUrl(mapWidth, mapHeight, mapCoordinates);
                String savedMapUrl = getSavedMapUrl();
                if (savedMapUrl != null && savedMapUrl.contentEquals(mapUrl)) {
                    if (setMapImage()) {
                        return;
                    }
                }
                deleteSavedMapUrl();
                File mapDirectory = getMapDirectory();
                File mapFile = getMapImageFile();
                Bitmap.CompressFormat mapFormat = Bitmap.CompressFormat.PNG;
                int mapQuality = 100;
                new DownloadImageTask(mapDirectory, mapFile, mapFormat, mapQuality).execute(mapUrl);
            }
        });
    }

    private void loadDynamicMap() {
        FragmentManager manager = getChildFragmentManager();
        SupportMapFragment mapFragment = (SupportMapFragment) manager.findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap map) {
        Activity activity = getActivity();
        DiveLogData data = DiveLogData.getInstance(activity);
        List<DiveLocation> locations = data.getDiverLocations(mDiver.getId());
        for(DiveLocation location : locations) {
            LatLng locationPosition = new LatLng(location.getLatitude(), location.getLongitude());
            MarkerOptions locationOptions = new MarkerOptions()
                    .position(locationPosition)
                    .title(location.getName())
                    .snippet(location.getCountry().getName());
            map.addMarker(locationOptions);
        }
        /*map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
            @Override
            public void onMapLongClick(LatLng latLng) {

            }
        });*/
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        Context context = getActivity();
        String diverId = Long.toString(mDiver.getId());
        Uri locationsUri = Uri.withAppendedPath(DiveLogProvider.CONTENT_URI, DiveLogProvider.PATH_DIVERS + "/" + diverId + "/locations");
        CursorLoader cursorLoader = new CursorLoader(
                context,
                locationsUri,
                null, // projection
                null, // selection
                new String[]{diverId}, // selectionArgs
                DiveLogDatabaseContract.DiveLocations.COLUMN_NAME_NAME); // sortOrder
        // Log.v(TAG, "Creating loader for diver with id = " + diverId);
        return cursorLoader;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    private class DownloadImageTask extends AsyncTask<String, Void, Boolean> {

        String mUrl;
        File mDirectory;
        File mFile;
        Bitmap.CompressFormat mFormat;
        int mQuality;

        public DownloadImageTask(File directory, File file, Bitmap.CompressFormat format, int quality) {
            this.mDirectory = directory;
            this.mFile = file;
            this.mFormat = format;
            this.mQuality = quality;
        }

        protected Boolean doInBackground(String... urls) {
            mUrl = urls[0];
            Bitmap bitmap = downloadBitmap(mUrl);
            if (bitmap == null) {
                Log.e(TAG, "Error downloading image: " + mUrl);
                return false;
            }
            return saveBitmapToFile(bitmap, mFile, mDirectory, mFormat, mQuality);
        }

        protected void onPostExecute(Boolean result) {
            if (result) {
                onImageDownloaded(mUrl);
            }
        }

        private Bitmap downloadBitmap(String url) {
            // Log.v(TAG, "downloadBitmap(url = " + url + ")");
            Bitmap bitmap = null;
            try {
                InputStream stream = new java.net.URL(url).openStream();
                bitmap = BitmapFactory.decodeStream(stream);
            } catch (Exception e) {
                Log.e(TAG, "Error downloading static map " + url + ": " + e.getMessage());
            }
            return bitmap;
        }

        private boolean saveBitmapToFile(Bitmap image, File file, File directory, Bitmap.CompressFormat format, int quality) {
            try {
                FileOutputStream fos = new FileOutputStream(file);
                if (image.compress(format, quality, fos)) {
                    createNomediaFile(directory);
                    return true;
                } else {
                    Log.e(TAG, "Error saving bitmap to compressed file");
                    return false;
                }
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Error saving file = " + file.getAbsolutePath() + ": " + e.getMessage());
                return false;
            }
        }

        private boolean createNomediaFile(File directory) {
            File nomedia = new File(directory, ".nomedia");
            try {
                nomedia.createNewFile();
            } catch (IOException e) {
                Log.e(TAG, "Error creating .nomedia file: " + e.getMessage());
                return false;
            }
            return true;
        }
    }
}